const fs = require('fs')
const path = require('path');
const vscode = require('vscode');
const protobuf = require('protobufjs');

/**
 * @param {string} filename
 */
function fileComment(filename) {
    var date = new Date(Date.now());
    var result = "--\n\
-- Created by Visual Studio Code.\n\
-- File:  ${filename}.lua\n\
-- Date: ${DATE_TIME}\n\
-- AUTHOR: lua_generator\n\
-- DESC: 描述\n\
--\n\n";
    return result
        .replace("${filename}", filename)
        .replace("${DATE_TIME}", date.toLocaleString());
}


/**
 * @param {string} msgName
 * @param {protobuf.Type} message
 */
function ProtoMessageToBaseLua(msgName, message) {
    return __ProtoMessageToBaseLua(msgName, message)
}


/**
 * @param {string} msgName
 * @param {protobuf.Type} message 
 */
function ProtoMessageToBaseLuaWithSet(msgName, message) {
    return __ProtoMessageToBaseLua(msgName, message, true)
}

/**
 * @param {string} msgName
 * @param {protobuf.Type} message
 * @param {boolean} needSet 
 */
 function __ProtoMessageToBaseLua(msgName, message, needSet = false) {
    var name = msgName.substr(0, msgName.length - 3);

    var result = fileComment(name);
    result += `---@class ${name} : ClassType\n`;
    result += `---@field public new ${name}.constructor @ 构造器, 查看构造函数回调__ctor\n`;
    message.fieldsArray.forEach((field, index, fieldsArray) => {
        var repeated = field.repeated ? "[]" : ""
        if (field.type.includes(".")) {
            var field_type = field.type.replace(".", "_pb.")
            result += `---@field public ${field.name} ${field_type}${repeated} @${field.comment}\n`;
        } else {
            result += `---@field public ${field.name} ${field.type}${repeated} @${field.comment}\n`;
        }
    });
    result += `---@field private _static_data table @ ${name}静态数据\n`;
    result += `local ${name} = Class.inheritsFrom("${name}")\n`;
    result += `local _M = ${name}\n\n`;

    result += `---@alias ${name}.constructor fun(static_data:table):${name}\n`;
    result += `---@param static_data table @ ${name}的静态数据，包含的字段和${name}中定义的字段相同\n`;
    result += `function _M:__ctor(static_data)\n`;
    result += `    self._static_data = static_data\n`;
    result += `end\n\n`;

    result += `-- ================ 属性======================\n`;
    if (!needSet){
        result += `---@type ${name}\n`;
        result += `local _get = Class.init_get(_M, true)\n\n`;
    } else {
        result += `---@type ${name}\n`;
        result += `local _get = Class.init_get(_M, true)\n`;
        result += `---@type ${name}\n`;
        result += `local _set = Class.init_set(_M, true)\n\n`;
    }

    message.fieldsArray.forEach((field, index, fieldsArray) => {
        if (!needSet){
            result += `---@param self ${name}\n`;
            result += `_get.${field.name} = function(self)\n`;
            if (field.type == 'string') {
                if (field.name == "icon") {
                    result += `    return "ui://icon/".. self._static_data.icon\n`;
                } else {
                    result += `    return languageData.i18n(self._static_data.${field.name})\n`;
                }
            } else {
                result += `    return self._static_data.${field.name}\n`;
            }
            result += `end\n\n`;
        } else {        
            result += `---@param self ${name}\n`;
            result += `_get.${field.name} = function(self)\n`;            
            result += `    return self._static_data.${field.name}\n`;            
            result += `end\n\n`;

            result += `---@param self ${name}\n`;
            result += `---@param val ${field.type}\n`;
            result += `_set.${field.name} = function(self, val)\n`;
            result += `    self._static_data.${field.name} = val\n`;
            result += `end\n\n`;
        }
    });

    result += `return _M`;
    return result;
}

/**
 * @param {string} msgName
 * @param {protobuf.Type} message
 */
function ProtoMessageToRoleLua(msgName, message) {
    var package = message.parent.name + "_pb"
    var name = msgName.substr(0, msgName.length - 3);
    var name_param = name[0].toLowerCase() + name.substr(1, msgName.length - 2) + "_";

    var result = fileComment(name);
    result += `---@class ${name} : ClassType\n`;
    result += `---@field public new ${name}.constructor @ 构造器, 查看构造函数回调__ctor\n`;
    message.fieldsArray.forEach((field, index, fieldsArray) => {
        var repeated = field.repeated ? "[]" : ""
        if (field.type.includes(".")) {
            var field_type = field.type.replace(".", "_pb.")
            result += `---@field public ${field.name} ${field_type}${repeated} @${field.comment}\n`;
        } else {
            result += `---@field public ${field.name} ${field.type}${repeated} @${field.comment}\n`;
        }
    });
    result += `local ${name} = Class.inheritsFrom("${name}")\n`;
    result += `local _M = ${name}\n\n`;

    result += `---@alias ${name}.constructor fun(${name_param} : ${package}.${msgName}):${name}\n`;
    result += `---@param ${name_param} ${package}.${msgName} @ ${name}的静态数据，包含的字段和${name}中定义的字段相同\n`;
    result += `function _M:__ctor(${name_param})\n`;
    result += `    self:syncData(${name_param})\n`;
    result += `end\n\n`;

    result += `---@param ${name_param} ${package}.${msgName}\n`;
    result += `function _M:syncData(${name_param})\n`;
    message.fieldsArray.forEach((field, index, fieldsArray) => {
        if (field.repeated) {
            result += `    self.${field.name} = ${name_param}.${field.name} or self.${field.name}\n`;
        } else if (field.type == 'int64') {            
            result += `    self.${field.name} = tonumber(${name_param}.${field.name}) or self.${field.name}\n`;
        } else if(field.type == "bool") {
            result += `    self.${field.name} = (${name_param}.${field.name} == nil and {self.${field.name}} or {${name_param}.${field.name}})[1]\n`;
        } else {
            result += `    self.${field.name} = ${name_param}.${field.name} or self.${field.name}\n`;
        }
    });
    result += `end\n\n`;

    result += `--- 生成对应的proto的消息结构\n`;
    result += `---@return ${package}.${msgName}\n`;
    result += `function _M:tomsg()\n`;
    result += `    local ${name_param} = ${package}.${msgName}()\n`;
    message.fieldsArray.forEach((field, index, fieldsArray) => {
        result += `    ${name_param}.${field.name} = self.${field.name}\n`;
    });
    result += `    return ${name_param}\n`;
    result += `end\n\n`;

    result += `-- ================ 属性======================\n`;    
    result += `--[=[\n`;
    result += `---@type ${name}\n`;
    result += `local _get = Class.init_get(_M, true)\n`;
    result += `---@type ${name}\n`;
    result += `local _set = Class.init_set(_M, true)\n`;
    result += `]=]\n\n`;
    

    result += `return _M`;
    return result;
}

/**
 * 1. proto 文件的公共提取函数，通过vscode选中的文本，提取到对于的proto message 
 * 2. 然后解析成protobuf对象，传递给回调函数，回调函数将的proto message映射成lua代码返回
 * 3. 然后该函数创建一个新的lua文档，让回调函数返回的lua代码写入文档中。
 * @param {*} callback
 */
function proto_to_lua(callback) {
    var editor = vscode.window.activeTextEditor;
    if (!editor) {
        return; // No open text editor
    }

    var selection = editor.selection;
    var selected_text = editor.document.getText(selection);
    var protoFile = editor.document.fileName;
    var options = {alternateCommentMode: true};
    var root = new protobuf.Root();
    root.load(protoFile, options, function(err, root) {
        if (err) {
            vscode.window.showErrorMessage(`无效的protobuf message类型名称：${err.message}`);
        }

        try {
            var selectedMsg = root.lookupType(selected_text);
            var options = {};
            options.language = "lua";
            options.content = callback(selected_text, selectedMsg);
            vscode.workspace.openTextDocument(options).then(document => {
                vscode.window.showTextDocument(document);
            });
        } catch (error) {
            vscode.window.showErrorMessage(`无效的protobuf message类型名称：${selected_text}`);
        }
    });
}

/**
 * 1. proto 文件的公共提取函数，提取当前proto文档的package
 * 2. 然后初始化package对应的模块目录
 * @param {*} callback
 */
 function proto_to_luaModule(callback) {
    var editor = vscode.window.activeTextEditor;
    if (!editor) {
        return; // No open text editor
    }

    var protoFile = editor.document.fileName;
    var options = {alternateCommentMode: true};
    var root = new protobuf.Root();
    root.load(protoFile, options, function(err, root) {
        if (err) {
            vscode.window.showErrorMessage(`无效的 protobuf 文件：${err.message}`);
        }

        try {
            var moduleName = path.basename(protoFile, ".proto");
            var rootDir = vscode.workspace.rootPath;
            if (vscode.workspace.workspaceFile != undefined) {
                vscode.workspace.workspaceFolders.forEach(element => {
                    var tmpRootdir = element.uri.fsPath;
                    var tmpDir = path.join(element.uri.fsPath, "Assets");
                    if (fs.existsSync(tmpDir)) {
                        rootDir = tmpRootdir;
                    }
                });
            }
            var templateDir = path.join(rootDir, ".lua_generator")
            var moduleRootdir = path.join(rootDir, "Assets\\LuaFramework\\Lua\\Logic")
            var moduleDir = path.join(moduleRootdir, moduleName)
            
            // 创建模块目录
            fs.exists(moduleDir, function(exists) {
                if(!exists){
                    fs.mkdir(moduleDir, function(err) {
                        if (err) {
                            vscode.window.showErrorMessage(`模块文件夹创建失败！: ${moduleDir} `);
                        } else {
                            vscode.window.showInformationMessage(`模块文件夹创建成功！: ${moduleDir} `);
                            copyTemplateFiles(templateDir, moduleDir, moduleName);
                        }
                    })
                } else {
                    vscode.window.showWarningMessage(`模块文件夹已经存在！: ${moduleDir} `);
                    copyTemplateFiles(templateDir, moduleDir, moduleName);
                }
            });
        } catch (error) {
            vscode.window.showErrorMessage(error.message);
        }
    });
}

/**
* @param {string} templateDir 模板目录路径
* @param {string} moduleDir 模块目录路径
*/
function copyTemplateFiles(templateDir, moduleDir, moduleName) {
    // 拷贝模块文件
    fs.exists(templateDir, function(exists) {
        if (!exists) {
            vscode.window.showInformationMessage(`模板文件夹不存在！: ${templateDir} `);
        } else {
            var files = fs.readdirSync(templateDir);
            const luaFiles = files.filter(el => /\$\{moduleName\}.*\.lua$/.test(el));
            luaFiles.forEach(el => {
                var dstFileName = el.replace("${moduleName}", moduleName);
                var dstFilePath = path.join(moduleDir, dstFileName);
                var srcFilePath = path.join(templateDir, el);
                if (!fs.existsSync(dstFilePath)) {
                    fs.copyFileSync(srcFilePath, dstFilePath);
                    // 替换内容
                    fs.readFile(dstFilePath, 'utf-8', function(err, data) {
                        if (err) {
                            return;
                        }
                        const search = /\$\{moduleName\}/;  
                        const replacer = new RegExp(search, 'g');

                        var newValue = data.replace(replacer, moduleName);
                        fs.writeFile(dstFilePath, newValue, "utf-8", function(err, data) {
                            if (err) {
                                return;
                            }
                            console.log(`${dstFileName} Done!`)
                        });
                    });
                } else {
                    vscode.window.showWarningMessage(`文件已经存在！${dstFileName}\r\n${dstFilePath}`);
                }                
                vscode.workspace.openTextDocument(dstFilePath).then(document => {
                    vscode.window.showTextDocument(document, {preview: false});
                });
            })           
        }
    })
}

function gen_lua_for_edit() {
    var editor = vscode.window.activeTextEditor;

    var selection = editor.selection;
    var className = editor.document.getText(selection);

    if (!editor) {
        return; // No open text editor
    }
    var selected_text = editor.document.getText();
    //var start = selected_text.match(/--\[==\[UI_JinDuTiaoHong_battleNew/);
    var start = selected_text.match("--\\[==\\["+className);
    var end = selected_text.match(className+"\\]==\\]");
    if (start == null || end == null) {
        vscode.window.showErrorMessage(`未匹配到生成代码块：--[==[...]==]`);
        return;
    }
    var startIndex = start.index + 7 + className.length;
    var endIndex = end.index;

    try {
        var options = {};
        options.language = "lua";
        var pattern = /---@class\s(\w+)/;
        var result = fileComment(className);
        options.content = result + selected_text.slice(startIndex, endIndex);
        vscode.workspace.openTextDocument(options).then(document => {
            vscode.window.showTextDocument(document);
        });
    } catch (error) {
        vscode.window.showErrorMessage(`匹配生成代码块错误${error}`);
    }
}

function gen_lua_for_copy() {
    var editor = vscode.window.activeTextEditor;

    var selection = editor.selection;
    var className = editor.document.getText(selection);

    if (!editor) {
        return; // No open text editor
    }
    var selected_text = editor.document.getText();
    //var start = selected_text.match(/--\[==\[UI_JinDuTiaoHong_battleNew/);
    var start = selected_text.match("--\\[==\\["+className);
    var end = selected_text.match(className+"\\]==\\]");
    if (start == null || end == null) {
        vscode.window.showErrorMessage(`未匹配到生成代码块：--[==[...]==]`);
        return;
    }
    var startIndex = start.index + 7 + className.length;
    var endIndex = end.index;

    try {
        var result = fileComment(className);
        var content = result + selected_text.slice(startIndex, endIndex)
        vscode.env.clipboard.writeText(content);
        vscode.window.showInformationMessage(`${className}复制到剪贴板`);
    } catch (error) {
        vscode.window.showErrorMessage(`匹配生成代码块错误${error}`);
    }
}

// 初始化模块目录
function init_ModuleFolder() {
    proto_to_luaModule();
}

module.exports = function(context) {
    // 注册 proto_to_Baselua 命令
    context.subscriptions.push(vscode.commands.registerCommand('extension.proto_to_Baselua', () => {
        proto_to_lua(ProtoMessageToBaseLua);
    }));

    // 注册 proto_to_BaseluaWithSet 命令
    context.subscriptions.push(vscode.commands.registerCommand('extension.proto_to_BaseluaWithSet', () => {
        proto_to_lua(ProtoMessageToBaseLuaWithSet);
    }));

    // 注册 proto_to_Rolelua 命令
    context.subscriptions.push(vscode.commands.registerCommand('extension.proto_to_Rolelua', () => {
        proto_to_lua(ProtoMessageToRoleLua);
    }));

    // 注册 gen_lua_for_edit 命令
    context.subscriptions.push(vscode.commands.registerCommand('extension.gen_lua_for_edit', () => {
        gen_lua_for_edit();
    }));

    // 注册 gen_lua_for_copy 命令
    context.subscriptions.push(vscode.commands.registerCommand('extension.gen_lua_for_copy', () => {
        gen_lua_for_copy();
    }));

    // 注册 init_ModuleFolder 命令
    context.subscriptions.push(vscode.commands.registerCommand('extension.init_ModuleFolder', () => {
        init_ModuleFolder();
    }));
};